﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using MethodWorx.CMS.Providers;
using System.Reflection;
using System.Xml;
using MethodWorx.CMS.Services;

namespace MethodWorx.CMS.Configuration
{
    public class WebConfiguration : IConfigurationSectionHandler
    {
        private static WebConfiguration instance;

        static WebConfiguration()
        {
            instance = (WebConfiguration)ConfigurationManager.GetSection("mwcms/web");
        }

        public static WebConfiguration Instance
        {
            get
            {
                return instance;
            }
        }

        public string ProviderName { get; set; }

        #region IConfigurationSectionHandler Members

        public object Create(object parent, object configContext, System.Xml.XmlNode section)
        {
            this.InitializeProvider(section);

            return this;
        }

        private void InitializeProvider(System.Xml.XmlNode section)
        {
            var node = section.SelectSingleNode("provider");
            if (node == null)
                throw new ConfigurationException("Missing provider node, please specify a provider configuration");

            if (node.Attributes["type"] == null)
                throw new ConfigurationException("Missing provider type, please specify a provider type");

            try
            {
                var type = node.Attributes["type"].Value.Split(',')[0].Trim();
                var assembly = node.Attributes["type"].Value.Split(',')[1].Trim();
                //
                //  we have the type, so create an instance
                this.Provider = (ICmsDataProvider)Activator.CreateInstance(assembly, type).Unwrap();
                //
                //  now setup all the properties from the configuration;
                foreach (XmlNode attribute in node.Attributes)
                {
                    var propertyInfo = this.Provider.GetType().GetProperty(attribute.Name, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
                    if (propertyInfo != null)
                    {
                        propertyInfo.SetValue(this.Provider, attribute.Value, null);
                    }
                }


                //  ok, we have the provder
                this.Provider.Initialize();

                foreach(XmlNode service in section.SelectNodes("service"))
                {
                    var abstractType = service.Attributes["abstract"].Value.Split(',')[0].Trim();
                    var abstractAssembly = service.Attributes["abstract"].Value.Split(',')[1].Trim();

                    var concreteType = service.Attributes["concrete"].Value.Split(',')[0].Trim();
                    var concreteAssembly = service.Attributes["concrete"].Value.Split(',')[1].Trim();

                    var t1 =
                        AppDomain.CurrentDomain.GetAssemblies().Where(a => a.GetName().Name == abstractAssembly).
                            FirstOrDefault().GetType(abstractType);
                    var asm =
                        AppDomain.CurrentDomain.GetAssemblies().Where(a => a.GetName().Name == concreteAssembly).
                            FirstOrDefault();
                    if (asm == null)
                        asm = Assembly.Load(concreteAssembly);

                    var t2 = asm.GetType(concreteType);



                    //
                    //ok, we have the types now
                    //  so lets add them)
                    typeof (ICmsDataProvider).GetMethod("AddService").MakeGenericMethod(t1, t2).Invoke(this.Provider,
                                                                                                      BindingFlags.
                                                                                                          Public |
                                                                                                      BindingFlags.
                                                                                                          Instance, null, null, null);
                }
            }
            catch(Exception ex)
            {
                throw new ConfigurationException("Could not create provider type, please see inner exception details", ex);
            }
        }

        public ICmsDataProvider Provider
        {
            get;
            private set;
        }

        #endregion

        public object GetService(Type t)
        {
            if(t == typeof(ContentService))
            {
                return new ContentService(this.Provider);
            }
            if (t == typeof(ICmsDataProvider))
            {
                return this.Provider;
            }
            else
                throw new NotSupportedException("Dependency can not be resolved: " + t.Name);
        }
    }
}
